/*!
 * \file set_relation_dot_visitor.cc
 *
 * \brief implementation of the SetRelationDotVisitor
 *
 * \date Started: 7/6/2010
 * \date Last Modified: 10/1/2010
 * \authors Alan LaMielle
 * \authors Nicholas Jeanette
 *
 * Copyright (c) 2010, Colorado State University <br>
 * All rights reserved. <br>
 * See ../../COPYING for details. <br>
 */

#include <iegenlib/iegenlib.h>

#include <iostream>

using std::cout;
using std::endl;

namespace iegenlib{

/***************Dot Visitor for Build Sparse Constraint**************/
void SetRelationDotVisitor::connectSegs(int sta, int fin)
{

  if(!nodeStack.empty())
  {
    filestr << nodeStack.top() << ": f" << sta << " -> " << count << ": f"
      << fin<< endl;
  }
  nodeStack.push(count);
  count++;
}

void SetRelationDotVisitor::defaultIn(){}

void SetRelationDotVisitor::defaultOut()
{

  nodeStack.pop();

  if(nodeStack.empty())
  {
    filestr << "}" << endl;
  }

}



void SetRelationDotVisitor::SetIn(const Set* node){
  count = 0;
  filestr << "digraph SparseConstraints \n\t{ \n\n\t\tnode [shape=record];"
      << "\n\t\tgraph [rankdir=\"LR\"];\n\n";

  filestr << count << " [label =\" <f0> Set | <f1> mST: | <f2> mConSet: |"
      << " <f3> Arity: " << node->getArity() << "\"];\n";

  connectSegs(1,0);
}

void SetRelationDotVisitor::SetOut(const Set* node){

defaultOut();
}


void SetRelationDotVisitor::RelationIn(const Relation* node){
  count = 0;
  filestr << "digraph SparseConstraints \n\t{ \n\n\t\tnode [shape=record];"
      << "\n\t\tgraph [rankdir=\"LR\"];\n\n";

  filestr << count << " [label =\" <f0> Relation | <f1> mST: | <f2> mConSet: |"
    << " <f3> inArity: " << node->getInArity() << " | <f4> outArity: "
      << node->getOutArity() << " \"];\n";

  connectSegs(1,0);
}

void SetRelationDotVisitor::RelationOut(const Relation* node){

defaultOut();
}

void SetRelationDotVisitor::SymbolTableIn(SymbolTable* node){
   ST = node;
   symcount = 0;
   filestr << count << " [label =\" <f0> SymbolTable | <f1> mSymToSte: | <f3> "
      << "mTypeColToSTE: \"];\n";


  connectSegs(1,0);

   if(node->numSTE() != 0){
      filestr << count << " [label =\" ";

      int c = 0; //temp counter
      string tstring; //temp string to store all symbols for dot
      for(list<string>::const_iterator itr = node->getIdBegin();
             itr != node->getIdEnd();
                ++itr)
      {
   //used to get our count into the string for use in our temp string tstring
         std::stringstream s; s << c; string tmpCounter; s >> tmpCounter;
         tstring += "<f" + tmpCounter + "> " + (*itr) + " | ";
         c++;
      }
      tstring = tstring.substr(0,tstring.length()-2);
      filestr << tstring << "\"];\n";

   connectSegs(0,0);
   }
}

void SetRelationDotVisitor::SymbolTableOut(SymbolTable* node){
   if(node->numSTE() != 0){
      nodeStack.pop();
   }
   defaultOut();
}


void SetRelationDotVisitor::TupleVarIn(TupleVar* node){
  filestr << count << " [label =\" ";

  filestr << "<f0> TupleVar | <f1> mID: " << node->getId() << " | <f3> mCol: "
      << node->getCol() << "\"];" << endl;

  connectSegs(symcount,0);
symcount++;
}

void SetRelationDotVisitor::ExistVarIn(ExistVar* node){

  filestr << count << " [label =\" ";

  filestr << "<f0> ExistVar | <f1> mID: " << node->getId() << " | <f3> mCol: "
      << node->getCol() << "\"];" << endl;
  connectSegs(symcount,0);
symcount++;
}

void SetRelationDotVisitor::SymConstIn(SymConst* node){

 filestr << count << " [label =\" ";

  filestr << "<f0> Symbolic | <f1> mID: " << node->getId() << " | <f3> mCol: "
      << node->getCol() << "\"];" << endl;
  connectSegs(symcount,0);
symcount++;
}


void SetRelationDotVisitor::UFCallIn(UFCall* node){

   filestr << count << " [label =\" ";

   filestr << "<f0> UFCall | <f1> mID: " << node->getFullFunc(ST) << " | <f3> mCol: "
      << node->getCol() << "\"];\n";
  connectSegs(symcount,0);
symcount++;
}


void SetRelationDotVisitor::SRConjunctionIn(const SRConjunction* node){
   currConjBegin = node->tuplevarsBegin();
   currConjEnd = node->tuplevarsEnd();
   filestr << count << "[label=\" <f0> Conjunct | <f1> mTuple |"
      << " <f2> mEqualitySet | <f3> mInequalitySet\"];\n";

  connectSegs(2,0);
}

void SetRelationDotVisitor::SRConjunctionOut(const SRConjunction* node){

   int c = 0;
   string tstring = "[label=\" ";
   if(node->tuplevarsBegin() != node->tuplevarsEnd()){
      for(list<TupleType>::const_iterator itr = node->tuplevarsBegin();
           itr != node->tuplevarsEnd();
              ++itr)
      {
     //used to get our count into the string for use in our temp string tstring
         std::stringstream s; s << c; string tmpCounter; s >> tmpCounter;

         tstring += "<f" + tmpCounter + "> " + tmpCounter + ": | ";
         c++;
      }
      tstring = tstring.substr(0,tstring.length()-2) + "\"];\n";

      filestr << count << tstring;
      connectSegs(0,0);
      int segcount = 0;
      for(list<TupleType>::const_iterator itr = node->tuplevarsBegin();
           itr != node->tuplevarsEnd();
              ++itr)
      {
         if((itr)->isConst()){
            filestr << count << "[label=\" <f0> ConstVal | <f1> mVal: "
             << (itr)->getValue() << " \"];\n";
         }else{
            filestr << count << "[label=\" <f0> TupleVar | <f1> mID: "
             << (itr)->getName() << " \"];\n";
         }
         connectSegs(segcount++,0);
         nodeStack.pop();
      }
      nodeStack.pop();
   }
   defaultOut();
}

void SetRelationDotVisitor::SREqualityIn(const SREquality* node){

   formatPrint(true,node->getExpVec());

}

void SetRelationDotVisitor::SRInequalityIn(const SRInequality* node){

   formatPrint(false,node->getExpVec());

}

void SetRelationDotVisitor::formatPrint(bool eq, ExpVec expr){

   //count gets incremented so we need to keep track of the old count
   int tmpCount = count;
   int ie; //used to link arrows based on equality or inequality
   ie = eq ? 2 : 3; //2 and 3 are the field numbers
   connectSegs(ie,0);

   //streams for the negative constraints and one for overall
   stringstream constraint;
   stringstream negConstraint;


   string tups, exists, syms, funcs;
   tups = exists = syms = funcs = "[label=\"";
   int tupCount, existCount, symCount, funcCount;
   tupCount = existCount = symCount = funcCount = 0;
   bool tupFound, existFound, symFound, funcFound;
   tupFound = existFound = symFound = funcFound = false;

   if(currConjBegin != currConjEnd){
      for(int col = 0; col != expr.get_tuplevars_size(); ++col){
         coefficientHelper(&tupFound, &tupCount, &tups, &expr, ST->lookup(TUPLE, col), &constraint, &negConstraint, false);
      }

      for(int col = 0; col != expr.get_existvars_size(); ++col){
         coefficientHelper(&existFound, &existCount, &exists, &expr, ST->lookup(EXISTENTIAL, col), &constraint, &negConstraint, false);
      }

      for(int col = 0; col != expr.get_symconst_size(); ++col){
         coefficientHelper(&symFound, &symCount, &syms, &expr, ST->lookup(SYMBOLIC, col), &constraint, &negConstraint, false);
      }

      for(int col = 0; col != expr.get_ufcalls_size(); ++col){
         if(ST->lookup(UFCALL, col) != 0){ //Checks for duplicate
            coefficientHelper(&funcFound, &funcCount, &funcs, &expr, ST->lookup(UFCALL, col), &constraint, &negConstraint, false);
         }
      }

   //attaches the side nodes of coefficients
      if(tupFound){
         sideNodeHelper(&tups, 1);
      }
      if(existFound){
         sideNodeHelper(&exists, 2);
      }
      if(symFound){
         sideNodeHelper(&syms, 3);
      }
      if(funcFound){
         sideNodeHelper(&funcs, 4);
      }
   }



   string tempNeg (negConstraint.str());
   if(!(negConstraint.str()).empty() && (negConstraint.str()).at(0)=='+'){
      tempNeg = (negConstraint.str()).substr(1);
   }
   if(negConstraint.str().empty()){
      negConstraint << 0;
      tempNeg = negConstraint.str();
   }
   int tempConst = expr.get_const();

   if(eq){//equality
      if(tempConst != 0){
         if(tempConst >0){
            constraint <<"+" << tempConst << " =  " <<  tempNeg;
         }else{
            constraint << tempConst << " =  " <<  tempNeg;
         }
      }else{
         constraint << " = " << tempNeg;
      }
   }else{//inequality
      if(tempConst != 0){
         if(tempConst >0){
            constraint <<"+" << tempConst << " \\>=  " <<  tempNeg;
         }else{
            if(tempNeg.at(0)=='0'){
               constraint << " \\>=  " << (-1*tempConst);
            }else{
               constraint << " \\>=  " <<  tempNeg << "+" << (-1*tempConst);
            }
         }
      }else{
         constraint << " \\>= " << tempNeg;
      }
   }

   string tempPos (constraint.str());
   if(!(constraint.str()).empty() && (constraint.str()).at(0)=='+'){
      tempPos = (constraint.str()).substr(1);
   }
   filestr << tmpCount << " [label=\" <f0> ExpVec for " << tempPos << " | "
      << " <f1> tuplevars_coeffs: | <f2> existvars_coeffs: |"
      << " <f3> symconst_coeffs: | <f4> ufcalls_coeffs: | <f5> const_coeff: "
      << tempConst <<"\"];\n";
}

void SetRelationDotVisitor::coefficientHelper(bool* found, int* count, string* out, ExpVec* expr, STE* symbol, stringstream* constraint, stringstream* negConstraint, bool first){
   *found = true;
   stringstream numstream;
   numstream << (*count)++;
   UFCall* ufcall = dynamic_cast<UFCall*>(symbol);
   if(ufcall == 0){
      *out += "<f" + numstream.str() + "> " + symbol->getId() + ": ";
   }else{
      *out += "<f" + numstream.str() + "> " + ufcall->getFullFunc(ST) + ": ";
   }

   int tempCoeff = expr->get_coeff(symbol->getType(), symbol->getCol());
   stringstream coeffstream;
   coeffstream << tempCoeff;
   *out += coeffstream.str() += " | ";

   //used for the positive coefficient
   if(tempCoeff > 0){
      //if not the first element and the coefficient is 1 just add +
      if(tempCoeff == 1 && !first){
         if(ufcall == 0){
            *constraint << "+"<<symbol->getId();
         }else{
            *constraint << "+"<<ufcall->getFullFunc(ST);
         }
      }else if(tempCoeff == 1){//if the first drop the 1
         if(ufcall == 0){
            *constraint << symbol->getId();
         }else{
            *constraint << ufcall->getFullFunc(ST);
         }
      }else{
         if(ufcall == 0){
            *constraint <<  "+" << coeffstream.str() << symbol->getId();
         }else{
            *constraint <<  "+" << coeffstream.str() << ufcall->getFullFunc(ST);
         }
      }
   }

   //used for the negative coefficient stores negative values
   //positively in negConstraint
   if(tempCoeff < 0){
      tempCoeff = 0 - tempCoeff;
      if(tempCoeff == 1 && !first){
         if(ufcall == 0){
            *negConstraint << "+"<<symbol->getId();
         }else{
            *negConstraint << "+"<<ufcall->getFullFunc(ST);
         }
      }else if(tempCoeff == 1){
         if(ufcall == 0){
            *negConstraint << symbol->getId();
         }else{
            *negConstraint << ufcall->getFullFunc(ST);
         }
      }else{
         if(ufcall == 0){
            *negConstraint << tempCoeff << symbol->getId();
         }else{
            *negConstraint << tempCoeff << ufcall->getFullFunc(ST);
         }
      }
   }
}

void SetRelationDotVisitor::sideNodeHelper(string* out, int nodeloc){
   *out = out->substr(0, out->size() - 2) + "\"];\n";
   filestr << count << *out;
   connectSegs(nodeloc,0);
   nodeStack.pop();
}

}//end namespace iegenlib
